/*! 
 * Terrain - Implementation file.
 *
 * \author Devis Rossini
 * \date 02 April 2012
 */
#include "PhxOgreTerrain.h"

using namespace GameObjects;

#define HEIGHTFIELD_ROWS	8
#define HEIGHTFIELD_COLUMNS	8

//-------------------------------------------------------------------------------------
Terrain::Terrain(phx::Scene* iScene, Ogre::SceneManager* iSceneManager, Ogre::SceneNode* iParentNode,
	const Ogre::String& iHeightMapName, const float iHorizontalScaleFactor, const float iVerticalScaleFactor) 
{
	// Create the physical object.
	mPhxEntity = CreatePhxEntity(	iScene, 
									iHeightMapName, 
									iHorizontalScaleFactor, 
									iVerticalScaleFactor);

	phx::CollisionTerrain* terrain	= static_cast<phx::CollisionTerrain*>(mPhxEntity);
	phx::HeightField heightField	= terrain->GetHeightField();

	//phx::real horizontalScaleFactor = terrain->GetHorizontalScaleFactor();
	//phx::real verticalScaleFactor	= terrain->GetVerticalScaleFactor();

	// Create the Ogre's entity associated to the ball and insert it into the scenegraph.
	mSceneNode = CreateOgreEntity(	iSceneManager, 
									iParentNode, 
									heightField, 
									iHorizontalScaleFactor,
									iVerticalScaleFactor);
}

//-------------------------------------------------------------------------------------
void Terrain::Update(const float iDuration)
{
	// Nothing to do...yet. ;)
}

//-------------------------------------------------------------------------------------
phx::CollisionPrimitive* Terrain::CreatePhxEntity(phx::Scene* iScene, const Ogre::String& iHeightMapName, const phx::real iHorizontalScaleFactor, const phx::real iVerticalScaleFactor)
{
	phx::CollisionTerrainDescriptor desc;

	if (iHeightMapName == "")
	{
		phx::HeightField heightField(HEIGHTFIELD_ROWS, HEIGHTFIELD_COLUMNS);
		SetRandomHeights(heightField);

		desc.mHeights = heightField;
	}
	else 
	{
		phx::HeightField heightField;
		PhxOgreUtils::TextureToHeightField(iHeightMapName, heightField);

		desc.mHeights = heightField;
	}

	desc.mHorizontalScaleFactor = iHorizontalScaleFactor;
	desc.mVerticalScaleFactor	= iVerticalScaleFactor;

	return iScene->CreatePrimitive(desc); 
}

//-------------------------------------------------------------------------------------
Ogre::SceneNode* Terrain::CreateOgreEntity(Ogre::SceneManager* iSceneManager, Ogre::SceneNode* iParentNode,
	const HeightField& iHeightField, const float iHorizontalScaleFactor, const float iVerticalScaleFactor)
{
	// Init a manual object (our terrain).
	Ogre::ManualObject* terrain = iSceneManager->createManualObject("Terrain");
	terrain->begin("Examples/GrassFloor", Ogre::RenderOperation::OT_TRIANGLE_LIST);

	size_t heightFieldRows	= iHeightField.GetRows();
	size_t heightFieldColumns = iHeightField.GetColumns();

	const float halfWidth	= (heightFieldColumns - 1)	* iHorizontalScaleFactor / 2.0f;
	const float halfHeight	= (heightFieldRows - 1)		* iHorizontalScaleFactor / 2.0f;

	// Load vertexes.
	for (size_t r = 0; r < heightFieldRows; ++r)
	{
		for (size_t c = 0; c < heightFieldColumns; ++c)
		{
			terrain->position(
				(c * iHorizontalScaleFactor) - halfWidth,
				iHeightField.GetHeight(r, c) * iVerticalScaleFactor,
				halfHeight - (r * iHorizontalScaleFactor));
			terrain->normal(0, 1, 0);
			terrain->textureCoord((float)c, (float)r);
		}
	}

	size_t i;

	// Set indexes.
	for (size_t r = 0; r < heightFieldRows - 1; ++r)
	{
		for (size_t c = 0; c < heightFieldColumns - 1; ++c)
		{
			if ((r % 2) == 0)
			{
				// Odd row.
				if ((c % 2) == 0)
				{
					// Odd column.
					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);

					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);
					i = r * heightFieldColumns + c + 1;
					terrain->index(i);
				}
				else
				{
					// Even column.
					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + c + 1;
					terrain->index(i);

					i = r * heightFieldColumns + c + 1;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);
				} 
			}
			else 
			{
				// Odd row.
				if ((c % 2) == 0)
				{
					// Odd column.
					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + c + 1;
					terrain->index(i);

					i = r * heightFieldColumns + c + 1;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);
				}
				else
				{
					// Even column.
					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);

					i = r * heightFieldColumns + c;
					terrain->index(i);
					i = r * heightFieldColumns + heightFieldColumns + c + 1;
					terrain->index(i);
					i = r * heightFieldColumns + c + 1;
					terrain->index(i);
				} 
			}
		}
	}

	terrain->end();
	terrain->convertToMesh("Terrain");

	// Create the Ogre's entity.
	Ogre::Entity* terrainEntity = iSceneManager->createEntity("Terrain");
	Ogre::SceneNode* terrainNode = iParentNode->createChildSceneNode("TerrainNode");
	terrainNode->attachObject(terrainEntity);

	return terrainNode;
}

//-------------------------------------------------------------------------------------
void Terrain::SetRandomHeights(phx::HeightField& oHeightField)
{
	srand(0);

	size_t rows		= oHeightField.GetRows();
	size_t columns	= oHeightField.GetColumns();

	size_t heightFieldSize = rows * columns;

	// Init an heightfield data structure with random values.
	phx::HeightFieldVertex borderVertex;
	borderVertex.mVertexHeight		= 0.0f;
	borderVertex.mVertexMaterial	= phx::Material::DEFAULT_MATERIAL;
	phx::HeightFieldData heightfieldData(heightFieldSize, borderVertex);

	oHeightField.SetVertexes(heightfieldData);

	for (size_t r = 1; r < rows - 1; ++r)
	{
		for (size_t c = 1; c < columns - 1; ++c)
		{
			phx::real height =  rand() / ((float)RAND_MAX);
			oHeightField.SetHeight(r, c, height);
		}
	}
}